package netnotifier;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;

import netnotifier.builder.HTTPBuilder;
import netnotifier.builder.PacketBuilder;
import netnotifier.builder.TCPBuilder;

public class NetNotifier
{
	public static NetNotifier instance=null;
	private HashMap<String,NetSniffer> sniffers=new HashMap<String,NetSniffer>();
	
	// the keys are classes
	// the values are callback lists
	// a callback is an array that contains an object, and a method
	private HashMap<Class<?>, ArrayList<ArrayList<Object>>> subscriptionMap=
		new HashMap<Class<?>, ArrayList<ArrayList<Object>>>();
	
	// the constructor
	public NetNotifier()
	{
		if(instance==null)instance=this;
		new PacketBuilder();
		new TCPBuilder();
		new HTTPBuilder();
	}
	
	public void shutdown()
	{
		for(NetSniffer sniffer:sniffers.values().toArray(new NetSniffer[]{}))
		{
			sniffer.stop();
		}
	
		sniffers=null;
		subscriptionMap=null;	
		instance=null;
	}
	
	public String[] getDeviceList()
	{
		return NetSniffer.getDeviceList();
	}
	
	public boolean startSniffer(String deviceName)
	{
		try
		{
			if(sniffers.containsKey(deviceName))return false;
			sniffers.put(deviceName,new NetSniffer(deviceName));
		}
		catch(Exception e)
		{
			return false;
		}
		return true;
	}
	
	public boolean stopSniffer(String deviceName)
	{
		try
		{
			if(sniffers.containsKey(deviceName)==false)return false;
			sniffers.get(deviceName).stop();
			sniffers.remove(deviceName);
		}
		catch(Exception e)
		{
			return false;
		}
		return true;
	}
	
	public void contribute(final Object object)
	{
		//System.out.println("contribute called");
		// find a list of the methods that care about this object
		try
		{
			Class objectClass=object.getClass();
			ArrayList<ArrayList<Object>> notificationList=subscriptionMap.get(objectClass);
			if(notificationList==null)
			{
				return;
			}
			final Object[] notificationArray=notificationList.toArray();
	
			// create a new thread to deal with new contributions
			/*new Thread()
			{
				public void run()
				{*/
					for(Object callback:notificationArray)
					{
						Object[] callbackArray=((ArrayList)callback).toArray();
						Object callbackObject=callbackArray[0];
						Method callbackMethod=(Method)callbackArray[1];
						try
						{
							callbackMethod.invoke(callbackObject,object);
						}
						catch (Exception e)
						{
							e.printStackTrace();
						}
					}
				/*}
			}.start();*/
		}
		catch(Exception e)
		{
			e.printStackTrace();
		}
	}
	
	// alert "callbackObject" with callbackMethod() every time a new object
	// of type "objectClass" enters the notifier
	public boolean subscribe(Class<?> objectClass, Object callbackObject, String callbackMethodName)
	{
		Method callbackMethod=null;
		ArrayList<Object> callback=new ArrayList<Object>();
		ArrayList<ArrayList<Object>> callbackList=null;
		
		try
		{
			callbackMethod=callbackObject.getClass().getMethod(callbackMethodName,Object.class);
			callback.add(0,callbackObject);
			callback.add(1,callbackMethod);
		}
		catch(Exception e)
		{
			return false;	
		}
		
		if(subscriptionMap.containsKey(objectClass))
		{	
			// get the callback list, and add the new callback
			subscriptionMap.get(objectClass).add(callback);
		}
		else
		{
			callbackList=new ArrayList<ArrayList<Object>>();
			callbackList.add(callback);
			subscriptionMap.put(objectClass,callbackList);
		}
		
		return true;
	}
}